package com.example.springboottest.example.react.singleThreadTest;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

//反应器

/**
 * 单线程 Reactor 反应器例子
 * 是一个基于反应器模式的 EchoServer 回显服务器的完整实现。它是一个单线程
 * 版本的反应器模式，Reactor 反应器和所有的 Handler 处理器，都执行在同一条线程中
 *
 * 缺点
 * 单线程 Reactor 反应器模式，是基于 Java 的 NIO 实现的。相对于传统的多线程 OIO，反应器
 * 模式不再需要启动成千上万条线程，效率自然是大大提升了。
 * 在单线程反应器模式中，Reactor 反应器和 Handler 处理器，都执行在同一条线程上。这样，
 * 带来了一个问题：当其中某个 Handler 阻塞时，会导致其他所有的 Handler 都得不到执行。在这种
 * 场景下，如果被阻塞的 Handler 不仅仅负责输入和输出处理的业务，还包括负责连接监听的
 * AcceptorHandler 处理器。这个是非常严重的问题。
 * 为什么？一旦 AcceptorHandler 处理器阻塞，会导致整个服务不能接收新的连接，使得服务器
 * 变得不可用。因为这个缺陷，因此单线程反应器模型用得比较少。
 * 另外，目前的服务器都是多核的，单线程反应器模式模型不能充分利用多核资源。总之，在
 * 高性能服务器应用场景中，单线程反应器模式实际使用的很少。
 */
class EchoServerReactor implements Runnable {
    public static final String IP = "localhost";
    public static final Integer PORT = 9999;
    public static final Integer BUFFER_SIZE = 10 * 1024;//一次传输多少  256Kb
    Selector selector;
    ServerSocketChannel serverSocket;

    EchoServerReactor() throws IOException {
        //Reactor初始化
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();

        InetSocketAddress address =
                new InetSocketAddress(IP,
                        PORT);
        serverSocket.socket().bind(address);
        System.out.println("服务端已经开始监听：" + address);
        //非阻塞
        serverSocket.configureBlocking(false);

        //分步处理,第一步,接收accept事件
        SelectionKey sk =
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        //attach callback object, AcceptorHandler
        sk.attach(new AcceptorHandler());
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set<SelectionKey> selected = selector.selectedKeys();
                Iterator<SelectionKey> it = selected.iterator();
                while (it.hasNext()) {
                    //Reactor负责dispatch收到的事件
                    SelectionKey sk = it.next();
                    dispatch(sk);
                }
                selected.clear();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    void dispatch(SelectionKey sk) {
        Runnable handler = (Runnable) sk.attachment();
        //调用之前attach绑定到选择键的handler处理器对象
        if (handler != null) {
            handler.run();
        }
    }

    // Handler:新连接处理器
    class AcceptorHandler implements Runnable {
        public void run() {
            try {
                SocketChannel channel = serverSocket.accept();
                System.out.println("接收到一个连接");
                if (channel != null)
                    new EchoHandler(selector, channel);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) throws IOException {
        new Thread(new EchoServerReactor()).start();
    }
}
